// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package lockedfile

import (
	"fmt"
	"os"
	"sync"
)

// 互斥锁通过锁定
// 著名的档案。这样的文件通常会保护文件的其他部分
// 文件系统：例如，目录中的互斥文件可能会保护对
// 整个树都根在该目录中。
// None
// 互斥锁不实现sync.Locker：与sync.Mutex不同，它是一个lockedfile.Mutex
// 可能无法锁定（例如，如果文件系统中存在权限错误）。
// None
// 与sync.Mutex类似，Mutex可以作为更大结构的字段包含，但
// 首次使用后不得复制。路径字段必须在第一个字段之前设置
// 使用，以后不得更改。
type Mutex struct {
	Path string     // 已知锁定文件的路径。必须为非空。
	mu   sync.Mutex // 冗余互斥锁。种族检测器不知道文件锁定，所以在测试中我们可能需要锁定它理解的东西。
}

// MutexAt返回一个新的互斥体，其路径设置为给定的非空路径。
func MutexAt(path string) *Mutex {
	if path == "" {
		panic("lockedfile.MutexAt: path must be non-empty")
	}
	return &Mutex{Path: path}
}

func (mu *Mutex) String() string {
	return fmt.Sprintf("lockedfile.Mutex(%s)", mu.Path)
}

// Lock尝试锁定互斥锁。
// None
// 如果成功，Lock将返回一个非nil unlock函数：它作为
// 返回值，而不是提醒调用方检查
// 伴随错误。（见https:
func (mu *Mutex) Lock() (unlock func(), err error) {
	if mu.Path == "" {
		panic("lockedfile.Mutex: missing Path during Lock")
	}

	// 我们可以在这里使用O_RDWR或O_WRONLY。如果我们选择O_RDWR和
	// Path处的文件是只写的，对OpenFile的调用将以
	// 权限错误。这实际上就是我们想要的：如果我们添加一个RLock方法
	// 将来，它应该只使用O_RDONLY调用OpenFile，并且需要
	// 文件必须是可读的，所以我们不应该让调用者做任何修改
	// 关于互斥体使用只写文件的假设。
	f, err := OpenFile(mu.Path, os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		return nil, err
	}
	mu.mu.Lock()

	return func() {
		mu.mu.Unlock()
		f.Close()
	}, nil
}
